package com.web3.management.service;

import com.web3.management.dto.PageResponse;
import com.web3.management.dto.request.AccountRequest;
import com.web3.management.dto.response.AccountClientTokenResponse;
import com.web3.management.dto.response.AccountDetailResponse;
import com.web3.management.dto.response.AccountListResponse;
import com.web3.management.entity.Account;
import com.web3.management.exception.BusinessException;
import com.web3.management.entity.AccountCredentialLog;
import com.web3.management.entity.TmClient;
import com.web3.management.repository.AccountConfigRepository;
import com.web3.management.repository.AccountRepository;
import com.web3.management.util.DataEncryptor;
import com.web3.management.security.AccountClientTokenUtils;
import com.web3.management.security.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 账号服务类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AccountService {

    private final AccountRepository accountRepository;
    private final AccountConfigRepository accountConfigRepository;
    private final DataEncryptor dataEncryptor;
    private final OperationLogService operationLogService;
    private final TmClientService tmClientService;
    private final AccountCredentialService accountCredentialService;
    private final TmCommandService tmCommandService;

    private static final long CLIENT_TOKEN_VALIDITY_DAYS = 365;

    public Account getById(Integer id) {
        return accountRepository.findById(id)
                .orElseThrow(() -> new BusinessException("账号不存在"));
    }

    public List<Account> getAllActiveAccounts() {
        return accountRepository.findByStatus(1);
    }

    public PageResponse<AccountListResponse> getAccountPage(Integer page, Integer size, String name, Integer status) {
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "updatedAt"));
        Page<Account> accountPage;

        if (StringUtils.hasText(name) && status != null) {
            accountPage = accountRepository.findByNameContainingIgnoreCaseAndStatus(name, status, pageable);
        } else if (StringUtils.hasText(name)) {
            accountPage = accountRepository.findByNameContainingIgnoreCase(name, pageable);
        } else if (status != null) {
            accountPage = accountRepository.findByStatus(status, pageable);
        } else {
            accountPage = accountRepository.findAll(pageable);
        }

        List<Account> accounts = accountPage.getContent();
        List<Integer> accountIds = accounts.stream().map(Account::getId).collect(Collectors.toList());
        java.util.Map<Integer, TmClient> clientMap = tmClientService.findByAccountIds(accountIds);
        java.util.Map<Integer, AccountCredentialLog> sessionMap =
                accountCredentialService.findLatestByAccountIds(accountIds, AccountCredentialService.CAPTURE_TYPE_SESSION);

        List<AccountListResponse> list = accounts.stream()
                .map(account -> convertToListResponse(account, clientMap, sessionMap))
                .collect(Collectors.toList());

        return PageResponse.of(accountPage.getTotalElements(), page, size, list);
    }

    public AccountDetailResponse getAccountDetail(Integer id) {
        Account account = getById(id);
        SecurityUtils.checkAccountAccess(id);
        return convertToDetailResponse(account);
    }

    @Transactional
    public AccountClientTokenResponse generateClientToken(Integer accountId) {
        SecurityUtils.requireAdmin();

        Account account = getById(accountId);

        String rawKey = AccountClientTokenUtils.generateRawKey();
        String token = AccountClientTokenUtils.buildToken(accountId, rawKey);
        String hashed = AccountClientTokenUtils.hashRawKey(rawKey);

        Instant now = Instant.now();
        Instant expiresAt = now.plus(CLIENT_TOKEN_VALIDITY_DAYS, ChronoUnit.DAYS);

        account.setClientTokenHash(hashed);
        account.setClientTokenIssuedAt(Date.from(now));
        account.setClientTokenExpiresAt(Date.from(expiresAt));
        accountRepository.save(account);

        operationLogService.record("GENERATE_ACCOUNT_TOKEN", "ACCOUNT", accountId,
                String.format("生成账号 %s 的客户端访问Token", account.getName()));

        return new AccountClientTokenResponse(token, account.getClientTokenExpiresAt());
    }

    @Transactional
    public AccountDetailResponse createAccount(AccountRequest request) {
        if (accountRepository.existsByName(request.getName())) {
            throw new BusinessException("账号名称已存在");
        }

        Account account = new Account();
        applyRequest(account, request);

        Account savedAccount = accountRepository.save(account);
        operationLogService.record("CREATE_ACCOUNT", "ACCOUNT", savedAccount.getId(),
                String.format("创建账号 %s", savedAccount.getName()));
        return convertToDetailResponse(savedAccount);
    }

    @Transactional
    public AccountDetailResponse updateAccount(Integer id, AccountRequest request) {
        Account existingAccount = getById(id);

        if (!existingAccount.getName().equals(request.getName()) &&
                accountRepository.existsByNameAndIdNot(request.getName(), id)) {
            throw new BusinessException("账号名称已存在");
        }

        applyRequest(existingAccount, request);

        // 账号信息变更时，设置需要重启标志
        existingAccount.setNeedsRestart(true);

        Account savedAccount = accountRepository.save(existingAccount);
        operationLogService.record("UPDATE_ACCOUNT", "ACCOUNT", savedAccount.getId(),
                String.format("更新账号 %s", savedAccount.getName()));
        return convertToDetailResponse(savedAccount);
    }

    /**
     * 标记账号需要重启
     */
    @Transactional
    public void markNeedsRestart(Integer accountId) {
        Account account = getById(accountId);
        account.setNeedsRestart(true);
        accountRepository.save(account);

        operationLogService.record("MARK_NEEDS_RESTART", "ACCOUNT", accountId,
                String.format("标记账号 %s 需要重启", account.getName()));

        log.info("标记账号需要重启: id={}, name={}", accountId, account.getName());
    }

    /**
     * 确认重启完成，清除重启标志并更新重启时间
     */
    @Transactional
    public void confirmRestart(Integer accountId) {
        Account account = getById(accountId);
        account.setNeedsRestart(false);
        account.setLastRestartTime(new Date());
        accountRepository.save(account);

        operationLogService.record("CONFIRM_RESTART", "ACCOUNT", accountId,
                String.format("账号 %s 确认重启完成", account.getName()));

        log.info("账号重启确认完成: id={}, name={}", accountId, account.getName());
    }

    /**
     * 上报客户端错误信息
     */
    @Transactional
    public void reportError(Integer accountId, String errorMessage, String errorType) {
        Account account = getById(accountId);
        account.setLastErrorMessage(errorMessage);
        account.setLastErrorTime(new Date());
        accountRepository.save(account);

        operationLogService.record("REPORT_ERROR", "ACCOUNT", accountId,
                String.format("账号 %s 上报错误: %s", account.getName(), errorType));

        log.error("账号错误上报: id={}, name={}, error={}", accountId, account.getName(), errorMessage);
    }

    @Transactional
    public void deleteAccount(Integer id) {
        Account account = getById(id);
        accountConfigRepository.deleteByAccountId(id);
        accountRepository.delete(account);
        operationLogService.record("DELETE_ACCOUNT", "ACCOUNT", id,
                String.format("删除账号 %s", account.getName()));
        log.info("删除账号成功: id={}", id);
    }

    public long countByStatus(Integer status) {
        return accountRepository.countByStatus(status);
    }

    private void applyRequest(Account account, AccountRequest request) {
        account.setName(request.getName());
        account.setStatus(request.getStatus());
        account.setCookies(request.getCookies());
        account.setCsrfToken(request.getCsrfToken());
        account.setProxyConfig(request.getProxyConfig());
        account.setRemark(request.getRemark());
    }

    private AccountListResponse convertToListResponse(Account account,
                                                     java.util.Map<Integer, TmClient> clientMap,
                                                     java.util.Map<Integer, AccountCredentialLog> sessionMap) {
        AccountListResponse response = new AccountListResponse();
        response.setId(account.getId());
        response.setName(account.getName());
        response.setStatus(account.getStatus());
        response.setHasCookies(StringUtils.hasText(account.getCookies()));
        response.setConfigCount(Math.toIntExact(accountConfigRepository.countByAccountId(account.getId())));
        response.setActiveConfigCount(Math.toIntExact(accountConfigRepository.countByAccountIdAndIsActive(account.getId(), 1)));

        // 获取激活配置的名称
        accountConfigRepository.findByAccountIdAndIsActive(account.getId(), 1)
                .ifPresent(config -> response.setActiveConfigName(config.getConfigName()));

        response.setRemark(account.getRemark());
        response.setUpdatedAt(account.getUpdatedAt());
        if (clientMap != null) {
            TmClient client = clientMap.get(account.getId());
            if (client != null) {
                response.setBoundClientId(client.getClientId());
                response.setClientLastSeenAt(client.getLastSeenAt());
            }
        }
        if (sessionMap != null) {
            AccountCredentialLog log = sessionMap.get(account.getId());
            if (log != null) {
                response.setLastCredentialCapturedAt(log.getCapturedAt());
            }
        }
        return response;
    }

    private AccountDetailResponse convertToDetailResponse(Account account) {
        AccountDetailResponse response = new AccountDetailResponse();
        response.setId(account.getId());
        response.setName(account.getName());
        response.setStatus(account.getStatus());
        response.setCookies(account.getCookies());
        response.setCsrfToken(account.getCsrfToken());
        response.setProxyConfig(account.getProxyConfig());
        response.setRemark(account.getRemark());
        response.setCreatedAt(account.getCreatedAt());
        response.setUpdatedAt(account.getUpdatedAt());
        response.setHasClientToken(StringUtils.hasText(account.getClientTokenHash()));
        response.setClientTokenExpiresAt(account.getClientTokenExpiresAt());

        // 设置重启相关字段
        response.setNeedsRestart(account.getNeedsRestart());
        response.setLastRestartTime(account.getLastRestartTime());

        // 设置错误相关字段
        response.setLastErrorMessage(account.getLastErrorMessage());
        response.setLastErrorTime(account.getLastErrorTime());

        tmClientService.findByAccountId(account.getId()).ifPresent(client -> {
            response.setBoundClientId(client.getClientId());
            response.setClientStatus(client.getStatus());
            response.setClientLastSeenAt(client.getLastSeenAt());
        });

        accountCredentialService.findLatest(account.getId(), AccountCredentialService.CAPTURE_TYPE_SESSION)
                .ifPresent(log -> response.setLastCredentialCapturedAt(log.getCapturedAt()));
        accountCredentialService.findLatest(account.getId(), AccountCredentialService.CAPTURE_TYPE_QR)
                .ifPresent(log -> response.setLastQrCapturedAt(log.getCapturedAt()));
        response.setPendingCommandCount((int) tmCommandService.countPendingByAccount(account.getId()));

        return response;
    }
}
